// ==UserScript==
// @name         B站分P批量下载SRT字幕
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  批量下载B站分P视频的SRT字幕，并显示详细流程进度
// @author       AI
// @match        https://www.bilibili.com/video/*
// @grant        GM_download
// @grant        GM_xmlhttpRequest
// ==/UserScript==

// 引入JSZip
const jszipScript = document.createElement('script');
jszipScript.src = 'https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js';
document.head.appendChild(jszipScript);

(function() {
    'use strict';

    // 用于保存所有SRT文件内容和文件名
    const allSrtFiles = [];

    // 悬浮窗
    const floatBox = document.createElement('div');
    floatBox.style = `
        position: fixed;
        right: 30px;
        bottom: 80px;
        width: 440px;
        max-height: 65vh;
        overflow-y: auto;
        background: #fff;
        color: #222;
        border-radius: 8px;
        box-shadow: 0 2px 12px rgba(0,0,0,0.18);
        z-index: 1000000;
        font-size: 14px;
        padding: 12px 18px 12px 18px;
        line-height: 1.7;
        display: none;
    `;
    floatBox.innerHTML = `<b>字幕批量下载进度</b><br><div id="bili-batch-log"></div>`;
    document.body.appendChild(floatBox);
    const logDiv = floatBox.querySelector('#bili-batch-log');

    // 新增：SRT下载链接悬浮窗口
    const srtBox = document.createElement('div');
    srtBox.style = `
        position: fixed;
        right: 470px; /* 紧贴floatBox的左侧 */
        bottom: 80px;
        width: 440px;
        max-height: 65vh;
        overflow-y: auto;
        background: #fff;
        color: #222;
        border-radius: 8px;
        box-shadow: 0 2px 12px rgba(0,0,0,0.18);
        z-index: 1000001;
        font-size: 14px;
        padding: 12px 18px 12px 18px;
        line-height: 1.7;
        display: none;
    `;
    srtBox.innerHTML = `<b>SRT字幕下载链接</b><br>
    <div id="bili-srt-link-list" style="max-height: calc(65vh - 150px); overflow-y: auto;"></div>
    <div id="srt-btn-bar" style="margin-top: 15px; border-top: 1px solid #eee; padding-top: 10px;">
      <button id="copy-all-srt-links" style="background:#f44336;color:#fff;border:none;border-radius:4px;padding:6px 12px;margin-right:8px;margin-bottom:5px;cursor:pointer;font-size:14px;">一键复制所有SRT字幕链接</button>
      <button id="download-all-srt-zip" style="background:#f44336;color:#fff;border:none;border-radius:4px;padding:6px 12px;margin-right:8px;margin-bottom:5px;cursor:pointer;font-size:14px;">一键打包下载所有SRT字幕</button>
      <button id="download-all-srt-browser" style="background:#4caf50;color:#fff;border:none;border-radius:4px;padding:6px 12px;cursor:pointer;font-size:14px;">逐个下载所有SRT字幕</button>
    </div>`;
    document.body.appendChild(srtBox);
    const srtLinkDiv = srtBox.querySelector('#bili-srt-link-list');
    const copyBtn = srtBox.querySelector('#copy-all-srt-links');
    const zipBtn = srtBox.querySelector('#download-all-srt-zip');
    const browserBtn = srtBox.querySelector('#download-all-srt-browser');
    if (copyBtn) {
        copyBtn.onclick = function() {
            const links = Array.from(srtLinkDiv.querySelectorAll('a')).map(a => a.href);
            if (links.length === 0) {
                alert('没有可复制的SRT字幕链接！');
                return;
            }
            const text = links.join('\n');
            navigator.clipboard.writeText(text).then(() => {
                copyBtn.textContent = '复制成功！';
                setTimeout(()=>{copyBtn.textContent = '一键复制所有SRT字幕链接';}, 1500);
            }, () => {
                alert('复制失败，请手动复制！');
            });
        };
    }
    if (zipBtn) {
        zipBtn.onclick = async function() {
            if (typeof JSZip === 'undefined') {
                alert('JSZip库未加载，请稍后再试！');
                return;
            }
            if (allSrtFiles.length === 0) {
                alert('没有可打包的SRT字幕！');
                return;
            }
            zipBtn.textContent = '正在打包...';
            zipBtn.disabled = true;
            const zip = new JSZip();
            allSrtFiles.forEach(f => {
                zip.file(f.filename, f.content);
            });
            const blob = await zip.generateAsync({type: 'blob'});
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = 'B站批量字幕.zip';
            document.body.appendChild(a);
            a.click();
            setTimeout(()=>{
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
            }, 1000);
            zipBtn.textContent = '打包完成！';
            setTimeout(()=>{
                zipBtn.textContent = '一键打包下载所有SRT字幕';
                zipBtn.disabled = false;
            }, 1500);
        };
    }
    if (browserBtn) {
        browserBtn.onclick = async function() {
            const links = Array.from(srtLinkDiv.querySelectorAll('a'));
            if (links.length === 0) {
                alert('没有可下载的SRT字幕！');
                return;
            }
            browserBtn.textContent = '正在下载...';
            browserBtn.disabled = true;
            for (let i = 0; i < links.length; i++) {
                links[i].click();
                await new Promise(res => setTimeout(res, 400)); // 避免浏览器拦截，间隔下载
            }
            browserBtn.textContent = '下载完成！';
            setTimeout(()=>{
                browserBtn.textContent = '逐个下载所有SRT字幕';
                browserBtn.disabled = false;
            }, 1500);
        };
    }

    function log(msg) {
        logDiv.innerHTML += msg + '<br>';
        floatBox.scrollTop = floatBox.scrollHeight;
    }
    function clearLog() {
        logDiv.innerHTML = '';
    }

    function addSrtLink(filename, url, srtContent) {
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        a.textContent = filename;
        a.style = 'color:#2196f3;display:block;margin-bottom:4px;word-break:break-all;';
        srtLinkDiv.appendChild(a);
        // 保存到allSrtFiles
        if (srtContent) {
            allSrtFiles.push({filename, content: srtContent});
        }
    }
    function clearSrtLinks() {
        srtLinkDiv.innerHTML = '';
        allSrtFiles.length = 0;
    }

    // 添加批量下载按钮
    const btn = document.createElement('button');
    btn.textContent = '批量下载SRT字幕';
    btn.style = 'position:fixed;right:30px;bottom:30px;z-index:999999;background:#00a1d6;color:#fff;border:none;border-radius:6px;padding:10px 16px;font-size:16px;cursor:pointer;';
    document.body.appendChild(btn);

    // 悬浮窗自动隐藏/显示逻辑
    let hideTimer = null;
    function showFloatWindows() {
        floatBox.style.display = 'block';
        srtBox.style.display = 'block';
        if (hideTimer) clearTimeout(hideTimer);
    }
    function hideFloatWindows() {
        floatBox.style.display = 'none';
        srtBox.style.display = 'none';
    }
    function scheduleHideFloatWindows() {
        if (hideTimer) clearTimeout(hideTimer);
        hideTimer = setTimeout(hideFloatWindows, 3000);
    }
    // 鼠标移入按钮时显示悬浮窗
    btn.addEventListener('mouseenter', showFloatWindows);
    // 鼠标移出按钮时3秒后隐藏悬浮窗
    btn.addEventListener('mouseleave', scheduleHideFloatWindows);
    // 鼠标移入悬浮窗时阻止隐藏
    floatBox.addEventListener('mouseenter', showFloatWindows);
    srtBox.addEventListener('mouseenter', showFloatWindows);
    // 鼠标移出悬浮窗时3秒后隐藏
    floatBox.addEventListener('mouseleave', scheduleHideFloatWindows);
    srtBox.addEventListener('mouseleave', scheduleHideFloatWindows);

    btn.onclick = async function() {
        let bv = location.pathname.match(/BV[\w]+/)[0];
        let pmax = prompt('请输入分P最大值（如30）', '30');
        let pmin = prompt('请输入分P起始值（如1）', '1');
        if (!bv || !pmax) return alert('参数有误');
        pmin = parseInt(pmin) || 1;
        pmax = parseInt(pmax);
        floatBox.style.display = 'block';
        clearLog();
        clearSrtLinks();
        srtBox.style.display = 'block'; // 启动时也显示SRT窗口
        log(`<span style="color:#888">BV号: ${bv}，分P范围: ${pmin} ~ ${pmax}</span>`);
        let success = 0, fail = 0;
        for (let p = pmin; p <= pmax; p++) {
            await downloadSRT(bv, p, p-pmin+1, pmax-pmin+1).then(ok=>{if(ok)success++;else fail++;});
            await new Promise(res => setTimeout(res, 1000)); // 停顿1秒
        }
        log(`<b style="color:#4caf50">全部处理完成！成功: ${success}，失败: ${fail}</b>`);
    };

    // 兼容fetch，优先用GM_xmlhttpRequest
    function gmFetch(url, options = {}) {
        return new Promise((resolve, reject) => {
            if (typeof GM_xmlhttpRequest === 'function') {
                GM_xmlhttpRequest({
                    method: options.method || 'GET',
                    url: url,
                    headers: options.headers || {},
                    responseType: options.responseType || '',
                    anonymous: options.credentials !== 'include',
                    onload: function (resp) {
                        const res = {
                            ok: resp.status >= 200 && resp.status < 300,
                            status: resp.status,
                            statusText: resp.statusText,
                            response: resp.response,
                            responseText: resp.responseText,
                            json: () => Promise.resolve(JSON.parse(resp.responseText)),
                            text: () => Promise.resolve(resp.responseText)
                        };
                        resolve(res);
                    },
                    onerror: function (err) {
                        reject(err);
                    }
                });
            } else {
                // 降级为原生fetch
                fetch(url, options).then(resolve).catch(reject);
            }
        });
    }

    async function downloadSRT(bv, p, idx, total) {
        let url = `https://www.bilibili.com/video/${bv}?p=${p}`;
        log(`<hr><span style="color:#2196f3">[${idx}/${total}] 处理分P: <a href="${url}" target="_blank">${url}</a></span>`);
        
        // 先尝试从API获取分P信息
        let partInfo = null;
        try {
            log(`→ 尝试从API获取分P信息...`);
            let apiUrl = `https://api.bilibili.com/x/player/pagelist?bvid=${bv}`;
            log(`→ 请求分P API: <a href="${apiUrl}" target="_blank">${apiUrl}</a>`);
            let pageData = await gmFetch(apiUrl).then(r=>r.json());
            await new Promise(res => setTimeout(res, 200)); // 0.2秒延迟
            if (pageData && pageData.code === 0 && pageData.data && Array.isArray(pageData.data)) {
                log(`√ 分P API返回成功，共${pageData.data.length}个分P`);
                let pageInfo = pageData.data.find(item => item.page == p);
                if (pageInfo && pageInfo.part) {
                    partInfo = pageInfo.part;
                    log(`√ 从API获取到分P标题: <b>${partInfo}</b>`);
                } else {
                    log(`× 未在API返回中找到当前分P信息`);
                }
            } else {
                log(`× 分P API返回错误: ${pageData?.message || '未知错误'}`);
            }
        } catch(e) {
            log(`× 获取分P API失败: ${e}`);
        }
        
        log(`→ 打开视频网页...`);
        let html;
        let curPage = p;
        try {
            log(`→ 获取HTML源码...`);
            html = await gmFetch(url).then(r=>r.text());
            await new Promise(res => setTimeout(res, 200)); // 0.2秒延迟
            log(`√ HTML获取成功，长度: ${html.length}`);
            // 解析当前分P序号
            let stateMatch = html.match(/<script>window\.__INITIAL_STATE__=({.*?});<\/script>/);
            if (stateMatch) {
                let state = JSON.parse(stateMatch[1]);
                curPage = state.p || (state.videoData && state.videoData.p) || p;
            }
        } catch(e) {
            log(`<span style="color:#f44336">× 获取页面失败: ${e}</span>`);
            return false;
        }
        log(`→ 提取cid参数...`);
        let cid = null;
        try {
            let stateMatch = html.match(/<script>window\.__INITIAL_STATE__=({.*?});<\/script>/);
            if (stateMatch) {
                let state = JSON.parse(stateMatch[1]);
                // 普通分P
                if (state.videoData && state.videoData.pages) {
                    let pageObj = state.videoData.pages.find(pg => pg.page == curPage);
                    if (pageObj) cid = pageObj.cid;
                }
                // cidMap
                if (!cid && state.cidMap && state.bvid) {
                    let cidMap = state.cidMap[state.bvid];
                    if (cidMap && cidMap.cids) {
                        let pageIdx = curPage - 1;
                        cid = cidMap.cids[pageIdx];
                    }
                }
                // epInfo
                if (!cid && state.epInfo && state.epInfo.cid) {
                    cid = state.epInfo.cid;
                }
            }
            // 2. __NEXT_DATA__（番剧/课程等）
            if (!cid) {
                let nextMatch = html.match(/<script id="__NEXT_DATA__" type="application\/json">(.*?)<\/script>/);
                if (nextMatch) {
                    let next = JSON.parse(nextMatch[1]);
                    let queries = next?.props?.pageProps?.dehydratedState?.queries;
                    if (Array.isArray(queries)) {
                        for (let q of queries) {
                            let eps = q?.state?.data?.seasonInfo?.mediaInfo?.episodes;
                            if (Array.isArray(eps)) {
                                let ep = eps.find(e => e?.page == curPage || e?.index == curPage);
                                if (ep && ep.cid) {
                                    cid = ep.cid;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        } catch(e) {}
        // 3. 兜底正则
        if (!cid) {
            let cidMatch = html.match(/"cid":(\d+),/);
            if (cidMatch) cid = cidMatch[1];
        }
        if (!cid) {
            log(`<span style="color:#f44336">× 未找到cid，跳过</span>`);
            return false;
        }
        log(`√ cid: <b>${cid}</b>`);
        // 主API多重兜底
        let apiList = [
            `https://api.bilibili.com/x/player/v2?cid=${cid}&bvid=${bv}`,
            `https://api.bilibili.com/x/player/wbi/v2?cid=${cid}&bvid=${bv}`
        ];
        let data = null;
        for (let api of apiList) {
            try {
                log(`→ 请求字幕API: <a href=\"${api}\" target=\"_blank\">${api}</a>`);
                data = await gmFetch(api, {credentials: 'include'}).then(r=>r.json());
                await new Promise(res => setTimeout(res, 200)); // 0.2秒延迟
                log(`√ 字幕API返回，code: ${data.code}`);
                if (data?.data?.subtitle?.subtitles?.length) break;
            } catch(e) {
                log(`<span style=\"color:#f44336\">× 获取字幕API失败: ${e}</span>`);
            }
        }
        let subs = data?.data?.subtitle?.subtitles;
        log('API返回的subtitle结构: ' + JSON.stringify(data?.data?.subtitle));
        if (!subs || !subs.length) {
            // 备用API兜底
            let aid = null;
            try {
                let stateMatch = html.match(/<script>window\.__INITIAL_STATE__=({.*?});<\/script>/);
                if (stateMatch) {
                    let state = JSON.parse(stateMatch[1]);
                    if (state.aid) aid = state.aid;
                    else if (state.videoData && state.videoData.aid) aid = state.videoData.aid;
                }
            } catch(e) {}
            if (!aid) {
                let aidMatch = html.match(/"aid":(\d+),/);
                if (aidMatch) aid = aidMatch[1];
            }
            let dmApi = `https://api.bilibili.com/x/v2/dm/view?${aid ? `aid=${aid}` : `bvid=${bv}`}&oid=${cid}&type=1`;
            log(`主API无字幕，尝试备用API: <a href=\"${dmApi}\" target=\"_blank\">${dmApi}</a>`);
            try {
                let dmData = await gmFetch(dmApi, {credentials: 'include'}).then(r=>r.json());
                await new Promise(res => setTimeout(res, 200)); // 0.2秒延迟
                log('备用API返回: ' + JSON.stringify(dmData?.data?.subtitle));
                subs = dmData?.data?.subtitle?.subtitles;
                if (!subs || !subs.length) {
                    log(`<span style=\"color:#f44336\">× 备用API也无CC字幕</span>`);
                    return false;
                }
            } catch(e) {
                log(`<span style=\"color:#f44336\">× 备用API请求失败: ${e}</span>`);
                return false;
            }
        }
        // 字幕优先级分层
        let sub =
            subs.find(s => s.lan && s.lan === 'zh-CN') // 第一层：中文
            || subs.find(s => s.lan && s.lan === 'zh-Hans')
            || subs.find(s => s.lan && s.lan === 'zh')
            || subs.find(s => s.lan && s.lan === 'zh-cn')
            || subs.find(s => s.lan && s.lan === 'zh-hans')
            || subs.find(s => s.lan && s.lan === 'zh-Hant')
            || subs.find(s => s.lan && s.lan === 'zh-hant')
            || subs.find(s => s.lan && s.lan === 'zh-TW')
            || subs.find(s => s.lan && s.lan === 'zh-tw')
            || subs.find(s => s.lan && s.lan === 'zh-HK')
            || subs.find(s => s.lan && s.lan === 'zh-hk')
            || subs.find(s => s.lan && s.lan === 'zh-MO')
            || subs.find(s => s.lan && s.lan === 'zh-mo')
            // 第二层：中文AI
            || subs.find(s => s.lan && s.lan === 'ai-zh')
            // 第三层：英文
            || subs.find(s => s.lan && (s.lan === 'en' || s.lan === 'en-US' || s.lan === 'en-GB'))
            || subs.find(s => s.lan && s.lan === 'ai-en')
            // 第四层：其他
            || subs[0];
        if (!sub) {
            log(`<span style=\"color:#f44336\">× 未找到可用字幕</span>`);
            return false;
        }
        log(`√ 选择字幕: <b>${sub.lan_doc || sub.lan}</b> | <a href=\"${sub.subtitle_url}\" target=\"_blank\" style=\"color:#00a1d6\">SRT原始链接</a>`);
        log(`→ 获取字幕JSON...`);
        let subData;
        try {
            let subUrl = sub.subtitle_url.replace(/^http:\/\//, 'https://');
            subData = await gmFetch(subUrl).then(r=>r.json());
            await new Promise(res => setTimeout(res, 200)); // 0.2秒延迟
            log(`√ 字幕JSON获取成功，条数: ${subData.body.length}`);
        } catch(e) {
            log(`<span style=\"color:#f44336\">× 获取字幕内容失败: ${e}</span>`);
            return false;
        }
        log(`→ 转换为SRT格式...`);
        let srt = toSRT(subData.body);
        log(`√ SRT转换完成，长度: ${srt.length}`);
        // 获取分P标题（优先<title>，兜底pages和document.title）
        let part = '';
        try {
            // 1. 优先使用pages结构中的分P标题
            let stateMatch = html.match(/<script>window\.__INITIAL_STATE__=({.*?});<\/script>/);
            if (stateMatch) {
                let state = JSON.parse(stateMatch[1]);
                log(`→ 调试: 解析INITIAL_STATE成功，检查videoData.pages`);
                if (state.videoData && state.videoData.pages) {
                    log(`→ 调试: 找到pages数组，长度: ${state.videoData.pages.length}`);
                    log(`→ 调试: 当前curPage=${curPage}, p=${p}`);
                    
                    // 打印所有页面信息用于调试
                    state.videoData.pages.forEach((pg, idx) => {
                        log(`→ 调试: 页面[${idx}] page=${pg.page}, part=${pg.part || '无标题'}`);
                    });
                    
                    let pageObj = state.videoData.pages.find(pg => pg.page == curPage);
                    if (pageObj) {
                        log(`→ 调试: 找到匹配页面，page=${pageObj.page}, part=${pageObj.part || '无标题'}`);
                        if (pageObj.part) part = pageObj.part;
                    } else {
                        log(`→ 调试: 未找到匹配页面，尝试用索引匹配`);
                        // 尝试用索引匹配
                        let pageIdx = curPage - 1;
                        if (pageIdx >= 0 && pageIdx < state.videoData.pages.length) {
                            pageObj = state.videoData.pages[pageIdx];
                            log(`→ 调试: 用索引找到页面，page=${pageObj.page}, part=${pageObj.part || '无标题'}`);
                            if (pageObj.part) part = pageObj.part;
                        }
                    }
                } else {
                    log(`→ 调试: 未找到videoData.pages结构`);
                }
                if (!part && state.epInfo && state.epInfo.title) {
                    part = state.epInfo.title;
                    log(`→ 调试: 使用epInfo.title: ${part}`);
                }
            } else {
                log(`→ 调试: 未找到INITIAL_STATE`);
            }
            
            // 2. 兜底__NEXT_DATA__结构
            if (!part) {
                let nextMatch = html.match(/<script id=\"__NEXT_DATA__\" type=\"application\/json\">(.*?)<\/script>/);
                if (nextMatch) {
                    log(`→ 调试: 找到__NEXT_DATA__结构`);
                    let next = JSON.parse(nextMatch[1]);
                    let queries = next?.props?.pageProps?.dehydratedState?.queries;
                    if (Array.isArray(queries)) {
                        for (let q of queries) {
                            let eps = q?.state?.data?.seasonInfo?.mediaInfo?.episodes;
                            if (Array.isArray(eps)) {
                                log(`→ 调试: 找到episodes数组，长度: ${eps.length}`);
                                let ep = eps.find(e => e?.page == curPage || e?.index == curPage);
                                if (ep) {
                                    log(`→ 调试: 找到匹配episode，title=${ep.title || '无标题'}`);
                                    if (ep.title) { part = ep.title; break; }
                                }
                            }
                        }
                    }
                } else {
                    log(`→ 调试: 未找到__NEXT_DATA__结构`);
                }
            }
            
            // 3. 最后才使用<title>标签
            if (!part) {
                // 尝试多种title格式匹配
                let titleMatches = [
                    html.match(/<title>([^_\n]+)_/),  // 标准格式：标题_UP主_哔哩哔哩
                    html.match(/<title>([^-\n]+) -/), // 另一种格式：标题 - 哔哩哔哩
                    html.match(/<title>([^|\n]+)\|/)  // 还有一种格式：标题|UP主|哔哩哔哩
                ];
                
                for (let match of titleMatches) {
                    if (match) {
                        part = match[1].trim();
                        log(`→ 调试: 使用<title>标签: ${part}`);
                        break;
                    }
                }
                
                if (!part) {
                    log(`→ 调试: 未找到<title>标签匹配`);
                }
            }
        } catch(e) {
            log(`→ 调试: 提取标题时出错: ${e.message}`);
        }
        if (!part) {
            // 从document.title中提取，处理多种可能的格式
            let title = document.title;
            log(`→ 调试: 使用document.title: ${title}`);
            
            // 处理多种可能的格式
            title = title
                .replace(/ - 哔哩哔哩.*/, '')  // "标题 - 哔哩哔哩"格式
                .replace(/_哔哩哔哩.*/, '')    // "标题_UP主_哔哩哔哩"格式
                .replace(/\|哔哩哔哩.*/, '')   // "标题|UP主|哔哩哔哩"格式
                .replace(/【.*?】/, '')        // 去掉【】标记
                .replace(/\[.*?\]/, '');       // 去掉[]标记
            
            // 如果标题包含 P1、P2 这样的分P标记，去掉它
            title = title.replace(/\s*P\d+\s*/, '')
                         .replace(/（P\d+）/, '')
                         .replace(/\(P\d+\)/, '');
            
            part = title.trim();
        }
        // 清理非法文件名字符
        part = part.replace(/[\\/:*?"<>|]/g, '').trim();
        
        // 如果之前从API获取到了分P标题，优先使用
        if (partInfo) {
            log(`√ 使用API获取的分P标题: <b>${partInfo}</b>`);
            part = partInfo;
        } else {
            log(`√ 使用网页提取的分P标题: <b>${part || '未找到，使用默认'}</b>`);
        }
        
        let newFilename = `p${p}_${part || ('分P'+p)}.srt`;
        let blob = new Blob([srt], {type: 'text/srt'});
        let urlObj = URL.createObjectURL(blob);
        // 新增：输出到SRT链接窗口，不自动下载
        addSrtLink(newFilename, urlObj, srt);
        log(`√ SRT链接已生成: <a href="${urlObj}" download="${newFilename}" style="color:#2196f3">${newFilename}</a>`);
        return true;
    }

    function toSRT(body) {
        return body.map((item, idx) => {
            let fmt = t => {
                let h = String(Math.floor(t/3600)).padStart(2,'0');
                let m = String(Math.floor(t%3600/60)).padStart(2,'0');
                let s = String(Math.floor(t%60)).padStart(2,'0');
                let ms = String(Math.floor((t-Math.floor(t))*1000)).padStart(3,'0');
                return `${h}:${m}:${s},${ms}`;
            };
            return `${idx+1}\n${fmt(item.from)} --> ${fmt(item.to)}\n${item.content}\n`;
        }).join('\n');
    }

    // JSZip加载完成后启用按钮
    jszipScript.onload = function() {
        if (zipBtn) {
            zipBtn.disabled = false;
            zipBtn.textContent = '一键打包下载所有SRT字幕';
        }
    };
})(); 